<?php
namespace App\Services\Pay;

use App\Contracts\PayContract;
use Exception;
use Illuminate\Support\Facades\Http;

class PayAli extends PayAliconfig implements PayContract
{
	public function status(){
		if($this->status && $this->appId && $this->seller_id && $this->alipayPublicKey && $this->merchantPrivateKey){
			return true;
		}else{
			return false;
		}
	}

	public function order($data=array()){
		if(!$this->status()){
			throw new Exception('Error setting_error');
		}
		$this->setReturnUrl($data['return_url']);
		$this->setTotalAmount($data['amount']);
		$this->setOutTradeNo($data['out_trade_no']);
		$this->setPassbackParams($data['origin']);
		$this->setSubject($data['title']);
		if(!empty($data['notify_url'])){
			$this->setNotifyUrl($data['notify_url']);
		}
		if($data['type']=='app'){
			return $this->pay(true);
		}else{
			return $this->pay();
		}
	}

	public function success(){
		exit('success');
	}

	public function fail($msg='fail'){
		exit($msg);
	}

	public function pay($app=false){
		if($app){
			$this->product_code='QUICK_MSECURITY_PAY';
			$this->returnUrl = '';
		}
		$requestConfigs = array(
			'out_trade_no'=>$this->outTradeNo,
			'product_code'=>$this->product_code,
			'total_amount'=>$this->totalAmount,
			'subject'=>$this->subject,
			'passback_params'=>$this->passback_params,
		);
		$commonConfigs = array(
			'app_id' => $this->appId,
			'method' => $this->method,
			'return_url' => $this->returnUrl,
			'charset'=>$this->charset,
			'sign_type'=>$this->signType,
			'timestamp'=>date('Y-m-d H:i:s'),
			'version'=>$this->version,
			'notify_url' => $this->notifyUrl,
			'biz_content'=>json_encode($requestConfigs),
		);
		$commonConfigs["sign"] = self::sign(self::getSignContent($commonConfigs), $this->merchantPrivateKey);
		if($app){
			return Http::post($this->getGatewayServerUrl(),$commonConfigs);
		}else{
			return $this->buildForm($this->getGatewayServerUrl(),$commonConfigs);
		}
	}

	public function query($out_trade_no, $trade_no=''){
		if(!$out_trade_no){
            throw new Exception('Error no_out_trade_no');
		}
		$requestConfigs = array(
			'out_trade_no'=>$out_trade_no,
			'trade_no'=>$trade_no,
		);
		$commonConfigs = array(
			'app_id' => $this->appId,
			'method' => 'alipay.trade.query',
			'sign_type'=>$this->signType,
			'charset'=>$this->charset,
			'timestamp'=>date('Y-m-d H:i:s'),
			'version'=>$this->version,
			'biz_content'=>json_encode($requestConfigs),
		);
		$commonConfigs["sign"] = self::sign(self::getSignContent($commonConfigs), $this->merchantPrivateKey);
		$result = Http::post($this->getGatewayServerUrl().'?charset='.$this->charset, $commonConfigs);
		$result = json_decode($result,true);
		return  $result['alipay_trade_query_response'];
	}

	public function refund($out_trade_no,$refund_amount, $trade_no=''){
		$this->setRefundAmount($refund_amount);
		$requestConfigs = array(
			'trade_no'=>$trade_no,
			'out_trade_no'=>$out_trade_no,
			'refund_amount'=>$this->refund_amount,
		);
		$commonConfigs = array(
			'app_id' => $this->appId,
			'method' => 'alipay.trade.refund',
			'charset'=>$this->charset,
			'sign_type'=>$this->signType,
			'timestamp'=>date('Y-m-d H:i:s'),
			'version'=>$this->version,
			'biz_content'=>json_encode($requestConfigs),
		);
		$commonConfigs["sign"] = self::sign(self::getSignContent($commonConfigs), $this->merchantPrivateKey);
		$result = Http::post($this->getGatewayServerUrl().'?charset='.$this->charset,$commonConfigs);
		$resultArr = json_decode($result,true);
		if($resultArr['alipay_trade_refund_response']['code'] && $resultArr['alipay_trade_refund_response']['code']=='10000'){
			return true;
		}else{
			return false;
		}
	}


	static public function sign($content, $privateKeyPem){
		$res = "-----BEGIN RSA PRIVATE KEY-----\n" .
			wordwrap($privateKeyPem, 64, "\n", true) .
			"\n-----END RSA PRIVATE KEY-----";
		if(!$res){
			throw new Exception('Error privateKeyPem_error');
		}
		openssl_sign($content, $sign, $res, OPENSSL_ALGO_SHA256);
		$sign = base64_encode($sign);
		return $sign;
	}


	static public function verify($content, $sign, $publicKeyPem){
		$res = "-----BEGIN PUBLIC KEY-----\n" .
			wordwrap($publicKeyPem, 64, "\n", true) .
			"\n-----END PUBLIC KEY-----";
		if(!$res){
			throw new Exception('Error publicKeyPem_error');
		}
		$result = FALSE;
		$result = (openssl_verify($content, base64_decode($sign), $res, OPENSSL_ALGO_SHA256) === 1);
		return $result;
	}

	public function notifyverify($parameters){
		return self::verifyParams($parameters,$this->alipayPublicKey);
	}

	static public function verifyParams($parameters, $publicKey){
		$sign = $parameters['sign'];
		$content = self::getSignContent($parameters,true);
		return self::verify($content, $sign, $publicKey);
	}

	static public function getSignContent($params,bool $verify=false){
		ksort($params);
		unset($params['sign']);
		if($verify){
			unset($params['sign_type']);
		}
		$ToBeSigned = "";
		$i = 0;
		foreach ($params as $k => $v) {
			if("@" != substr($v, 0, 1)) {
				if($i == 0) {
					$ToBeSigned .= "$k" . "=" . "$v";
				} else {
					$ToBeSigned .= "&" . "$k" . "=" . "$v";
				}
				$i++;
			}
		}
		unset ($k, $v);
		return $ToBeSigned;
	}

	static public function checkEmpty($value){
		if(!isset($value))
			return true;
		if($value === null)
			return true;
		if(trim($value) === "")
			return true;
		return false;
	}

	public function buildForm($actionUrl,$parameters){
		$sHtml = "<form id='alipaysubmit' name='alipaysubmit' action='" . $actionUrl . "?charset=" . trim($this->charset) . "' method='POST'>";
		while (list ($key, $val) = $this->fun_adm_each($parameters)) {
			if(false === $this->checkEmpty($val)) {
				$val = str_replace("'", "&apos;", $val);
				//$val = str_replace("\"","&quot;",$val);
				$sHtml .= "<input type='hidden' name='" . $key . "' value='" . $val . "'/>";
			}
		}
		$sHtml = $sHtml . "<input type='submit' value='ok' style='display:none;''></form>";
		$sHtml = $sHtml . "<script>document.forms['alipaysubmit'].submit();</script>";
		return $sHtml;
	}

	protected function fun_adm_each(&$array){
		$res = array();
		$key = key($array);
		if($key !== null) {
			next($array);
			$res[1] = $res['value'] = $array[$key];
			$res[0] = $res['key'] = $key;
		} else {
			$res = false;
		}
		return $res;
	}
}
